﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using DatabaseSchema.NvigorateDemo;
using Nvigorate.Data.Query;
using Nvigorate.Relational.Mapping.Fluent;

namespace Nvigorate.Demo.Domain
{
    public class CustomerMap : FluentMap<Customer>
    {
        public CustomerMap()
        {
            RepositoryName = "demo";
            var table = MapTable<CustomerSchema>();

            table.Assign(c => c.Id, t => t.CustomerId).AsKey().ReadOnly();
            table.Assign(c => c.FirstName);
            table.Assign(c => c.LastName);

            var contacts = MapTable<CustomerContactInformationSchema>();
            contacts.Assign(c => c.Id, t => t.CustomerId).AsKey();
            contacts.Assign(c => c.PrimaryEmail, t => t.PrimaryEmail).Nullable();
            contacts.Assign(c => c.SecondaryEmail, t => t.SecondaryEmail).Nullable();
            contacts.Assign(c => c.PrimaryPhone.Country, t => t.PrimaryCountryCode).ConstructorArgument(0).Nullable();
            contacts.Assign(c => c.PrimaryPhone.Area, t => t.PrimaryCountryCode).ConstructorArgument(1).Nullable();
            contacts.Assign(c => c.PrimaryPhone.Exchange, t => t.PrimaryAreaCode).ConstructorArgument(2).Nullable();
            contacts.Assign(c => c.PrimaryPhone.Number, t => t.PrimaryExchange).ConstructorArgument(3).Nullable();
            contacts.Assign(c => c.PrimaryPhone.Extension, t => t.PrimaryExtension).ConstructorArgument(4).Nullable();
            contacts.Assign(c => c.SecondaryPhone.Country, t => t.SecondaryCountryCode).ConstructorArgument(0).Nullable();
            contacts.Assign(c => c.SecondaryPhone.Area, t => t.SecondaryCountryCode).ConstructorArgument(1).Nullable();
            contacts.Assign(c => c.SecondaryPhone.Exchange, t => t.SecondaryAreaCode).ConstructorArgument(2).Nullable();
            contacts.Assign(c => c.SecondaryPhone.Number, t => t.SecondaryExchange).ConstructorArgument(3).Nullable();
            contacts.Assign(c => c.SecondaryPhone.Extension, t => t.SecondaryExtension).ConstructorArgument(4).Nullable();

            contacts.JoinTo(table, JoinType.Left,
                            contacts.Column(c => c.CustomerId).EqualTo(table.Column(t => t.CustomerId)));

            MapParent(c => c.MainAddress)
                .ForeignKey(c => c.Id, a => a.Id);

            MapToMany<IVehicle, DriversSchema>(c => c.Vehicles)
                .SelfToContainer(c => c.Id, d => d.CustomerId)
                .RelativeToContainer(v => v.Id, d => d.VehicleId);
        }
    }
    
    public class AddressMap : FluentMap<Address>
    {
        public AddressMap()
        {
            var table = MapTable<HomeAddressSchema>();

            table.Assign(a => a.Id, t => t.AddressId).AsKey().ReadOnly();
            table.Assign(a => a.Street1, t => t.AddressLine1);
            table.Assign(a => a.Street2, t => t.AddressLine2);
            table.Assign(a => a.City);
            table.Assign(a => a.State);
            table.Assign(a => a.Zip);

            MapChildren(a => a.Residents)
                .ForeignKey(a => a.Id, c => c.MainAddress.Id);
        }
    }

    public class AppointmentMap : FluentMap<Appointment>
    {
        public AppointmentMap()
        {
            var table = MapTable<AppointmentSchema>();
            
            table.Assign(a => a.Id, t => t.AppointmentId).AsKey().ReadOnly();
            table.Assign(a => a.Time, t => t.ScheduledFor);

            MapParent(a => a.Bill)
                .ForeignKey(a => a.Id, b => b.Appointment.Id);
        }
    }

    public class BillMap : FluentMap<Bill>
    {
        public BillMap()
        {
            RepositoryName = "demo";
            var table = MapTable<BillSchema>();

            table.Assign(b => b.Id, t => t.BillId).AsKey().ReadOnly();

            MapParent(b => b.Appointment)
                .ForeignKey(b => b.Appointment.Id, a => a.Id);

            MapChildren(b => b.LineItems)
                .ForeignKey(b => b.Id, l => l.Bill.Id);
        }
    }


    public class VehicleMap : FluentMap<IVehicle>
    {
        public VehicleMap()
        {
            RepositoryName = "demo";
            var table = MapTable<VehicleSchema>();
            table.Assign(v => v.Id, t => t.VehicleId).AsKey().ReadOnly();
            table.Assign(v => v.Make); // no need to provide the column if it's named the same
            table.Assign(v => v.Model);
            table.Assign(v => v.Color);
            table.Assign(v => v.Year, t => t.ModelYear);
            table.Assign(v => v.Type, t => t.VehicleTypeId);

            var type = MapTable<VehicleTypeSchema>()
                            .ForbidPersist(); // this blocks insert, update and delete
            type.Assign(v => v.Type, t => t.VehicleType);

            type.JoinTo(table, JoinType.Inner,
                        type.Column(t => t.VehicleTypeId).EqualTo(table.Column(v => v.VehicleTypeId)));

            MapToMany<Customer, DriversSchema>(v => v.Drivers)
                .SelfToContainer(v => v.Id, d => d.VehicleId)
                .RelativeToContainer(c => c.Id, d => d.CustomerId);

            

        }
    }

    public class CarMap : FluentMap<Car>
    {
        public CarMap()
        {
            InheritMap<VehicleMap, IVehicle>();
        }
    }

    public class TruckMap : FluentMap<Truck>
    {
        public TruckMap()
        {
            InheritMap<VehicleMap, IVehicle>();
        }
    }
}
